/**
 * N个人，M对互相认识
 * 问能选出多少个三元组(x,y,z)进行操作：
 * x和y、y和z互相认识但x和z不认识
 * 
 * 注意到x/y/z必然处在同一个连通块，但是x和z之间没有边，才符合条件
 * 一个n个节点的连通块最多能有n(n-1)/2条边，其中共有M条是题目给定的
 * 因此用一个带size的并查集即可
 */
#include <bits/stdc++.h>
using namespace std;

using llt = long long;
using vi = vector<int>;
using pii = pair<int, int>;
using vpii = vector<pii>;
using vll = vector<llt>;

template<typename T>
void input(vector<T> & a, int n){
    a.assign(n + 1, {});
    for(int i=1;i<=n;++i) cin >> a[i];
    return;
}

template<typename T>
istream & operator >> (istream & is, vector<T> & v){
    for(auto & i : v) is >> i;
    return is;
}

llt const MOD = 1E9+7;

struct _t{

vi father;
vi size;
void init(int n){father.assign(n + 1, 0); size.assign(n + 1, 1); for(int i=1;i<=n;++i)father[i]=i;}
int find(int x){return x == father[x] ? x : father[x] = find(father[x]);}
void unite(int x, int y){
    x = find(x);
    y = find(y);
    if(x == y) return;
    size[x] += size[y];
    father[y] = x;
}

}U;

int N, M;


void work(){
    cin >> N >> M;
    U.init(N);
    for(int a,b,i=0;i<M;++i){
        cin >> a >> b;
        U.unite(a, b);
    }
    llt ans = 0; 
    for(int i=1;i<=N;++i){
        if(i == U.find(i)){
            llt sz = U.size[i];
            ans += sz * (sz - 1) / 2;
        }
    }
    ans -= M;
    cout << ans << "\n";
    return;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("z.txt", "r", stdin);
#endif
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int nofkase = 1;
    // cin >> nofkase;
    while(nofkase--) work();
    return 0;
}